# (c) Copyright 2008 Paul Hankin. All Rights Reserved. 
import main
import graphics

"""Make human-readable versions of matches."""

class Reporter(object):
    "Abstract base class for creating backgammon reports"
    def match_header(self, match):
        "Lines produced at the start of a match."
        return []
    def match_footer(self, match):
        "Lines produced at the end of a match."
        return []
    def game_header(self, game):
        "Lines produced at the start of a game."
        return []
    def game_footer(self, game):
        "Lines produced at the end of a game."
        return []
    def move_header(self):
        "Lines produced at the start of a move."
        return []
    def move_footer(self):
        "Lines produced at the end of a move."
        return []
    # Which form should board.toString use?
    board_form = 'ascii'
    def draw_board(self, player, game_state):
        "Lines of a board."
        return getattr(graphics, 'toString_' + self.board_form)(
            player = player, **game_state.kwargs
        ).split('\n')
        
    def mover(self, game, move, preP=False, postP=False):
        """Report a move. preP and postP are hints that there should be
        a break in the report before or after the move."""
        return []
        
    # Lines produced by each of the possible actions in a game.
    def move_roll(self, game, move):
        return self.mover(game, move, postP=True)
    def move_move(self, game, move):
        return self.mover(game, move, preP=True, postP=True)
    def move_double(self, game, move):
        return self.mover(game, move, postP=True)
    def move_take(self, game, move):
        return self.mover(game, move, postP=True)
    def move_drop(self, game, move):
        return self.mover(game, move, preP=True)
    def move_resign(self, game, move):
        return self.mover(game, move, preP=True, postP=True)
    def move_win(self, game, move):
        return self.mover(game, move, preP=True)
    def move_winmatch(self, game, move):
        return self.mover(game, move, preP=True)

    # Which moves cause a move header to be printed
    starts_moves = (bg.GameMoveRoll, bg.GameMoveDouble,
        bg.GameMoveTake, bg.GameMoveDrop)
    # Which moves cause a move footer to be printed
    ends_moves = (bg.GameMoveMove, bg.GameMoveDouble,
        bg.GameMoveTake, bg.GameMoveDrop)
    # Which moves should display a board before the action is taken?
    pre_board = (bg.GameMoveMove, bg.GameMoveDrop)
    # Which moves should display a board after the action is taken?
    post_board = (bg.GameMoveMove, bg.GameMoveTake, bg.GameMoveDouble)
        
    def report_game(self, game):
        "Generate lines of a report of a game."
        # Game header
        for x in self.game_header(game): yield x
        # Move reports for each move
        for pre_state, move, post_state in game.replay():
            if isinstance(move, self.starts_moves):
                # Draw a move header
                for x in self.move_header(): yield x
            if isinstance(move, self.pre_board):
                # Draw a pre-move board
                for x in self.draw_board(move.player, pre_state): yield x
            # Call the user hook for the move.
            for x in getattr(self, 'move_' + move.action)(game, move):
                yield x
            if isinstance(move, self.post_board):
                # Draw a post-move board
                for x in self.draw_board(move.player, post_state): yield x
            if isinstance(move, self.ends_moves):
                # Draw a move footer
                for x in self.move_footer(): yield x
        # Game footer
        for x in self.game_footer(game): yield x

    def report_match(self, match):
        "Generate lines of a report of a match."
        # Match header
        for x in self.match_header(match): yield x
        # Game reports for each game
        for game in match:
            for x in self.report_game(game): yield x
        # Match footer
        for x in self.match_footer(match): yield x

class AsciiReporter(Reporter):
    "Create an ascii report of a backgammon game or match."
    @property
    def board_width(self):
        return len(graphics.toString_ascii(bg.initialPosition).split('\n')[0])

    def game_header(self, game):
        game_number, match_length = game.game_number, game.match_length
        yield 'Game %(game_number)d in a match to %(match_length)d' % locals()
        length = max(len(name) for name in game.players)
        for score, name in zip(game.score, game.players):
            yield '%*s : %d' % (length, name, score + game.match_length)

    def game_footer(self, game):
        return ['', '=' * self.board_width, '']

    def move_header(self):
        return ['', '-' * self.board_width, '']
        
    board_form = 'ascii'
    
    def mover(self, game, move, preP=False, postP=False):
        if preP: yield ''
        yield '%s %s' % (game.players[move.player], move)
        if postP: yield ''

class HtmlPlainReporter(AsciiReporter):
    "Create a fixed-width plain text report in html."
    def match_header(self, match):
        yield '<HTML><BODY><pre>'
    def match_footer(self, match):
        yield '</pre></BODY></HTML>'
    
class HtmlReporter(Reporter):
    "Create an html report of a backgammon game or match."
    board_form = 'html'
    def match_header(self, match):
        yield '<HTML><BODY>'
    def match_footer(self, match):
        yield '</BODY></HTML>'
    def game_header(self, game):
        game_number, match_length = game.game_number, game.match_length
        yield '<H2>Game %(game_number)d in a match to %(match_length)d</H2>' % locals()
        for name, score in zip(game.players, game.score):
            yield '%s : %d' % (name, score + match_length)
    def game_footer(self, game):
        yield '<BR><HR><HR><BR>'
    def move_header(self):
        yield '<BR><HR><BR>'
    def mover(self, game, move, preP=False, postP=False):
        yield '%s%s %s%s' % (preP * '<P>', game.players[move.player],
            move, postP * '<P>')
